﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CRMAdminDashboard.CrmSvc;
using System.Diagnostics;

namespace CRMAdminDashboard
{
    /// <summary>
    /// Create and represent relationships between SystemRoles
    /// </summary>
    public class RoleUserHierarchy
    {

        #region Fields

        /// <summary>
        /// Contains the subset of BUs the user has selected. If null, the user has selected all the BUs.
        /// </summary>
        private Dictionary<Guid, BusinessUnit> _buFilter;

        /// <summary>
        /// The CRM Service handle
        /// </summary>
        private CachingCrmService _svcHandle;

        /// <summary>
        /// All roles
        /// </summary>
        private Dictionary<Guid, SystemRole> _allRoles;

        /// <summary>
        /// All roles filtered by BU (only roots)
        /// </summary>
        private Dictionary<Guid, SystemRole> _filteredRoles;

        /// <summary>
        /// All users
        /// </summary>
        private Dictionary<Guid, SystemUser> _allUsers;

        /// <summary>
        /// All users filtered by BU
        /// </summary>
        private Dictionary<Guid, SystemUser> _filteredUsers;

        #endregion

        #region Properties

        /// <summary>
        /// Contains the subset of BUs the user has selected. If null, the user has selected all the BUs.
        /// </summary>
        public Dictionary<Guid, BusinessUnit> BuFilter
        {
            get
            {
                return _buFilter;
            }
            set
            {
                _buFilter = value;
                _filteredUsers = null;
                _filteredRoles = null;
            }
        }

        /// <summary>
        /// All roles
        /// </summary>
        public Dictionary<Guid, SystemRole> AllRoles
        {
            get
            {
                if (_allRoles == null)
                {
                    _allRoles = SystemRole.GetAllRoles(_svcHandle, null, SystemRole.SystemRoleColumnSet);
                    SetupRoleHierarchy();
                }
                return _allRoles;
            }
        }

        /// <summary>
        /// All roles filtered by BU (only roots)
        /// </summary>
        public Dictionary<Guid, SystemRole> FilteredRoles
        {
            get
            {
                if (_filteredRoles == null)
                {
                    _filteredRoles = GetFilteredRoles();
                }
                return _filteredRoles;
            }
        }

        /// <summary>
        /// All users
        /// </summary>
        public Dictionary<Guid, SystemUser> AllUsers
        {
            get
            {
                if (_allUsers == null)
                {
                    _allUsers = SystemUser.GetAllUsers(_svcHandle, SystemUser.SystemUserColumnSet);
                }
                return _allUsers;
            }
        }

        /// <summary>
        /// All users filtered by BU
        /// </summary>
        public Dictionary<Guid, SystemUser> FilteredUsers
        {
            get
            {
                if (_filteredUsers == null)
                {
                    _filteredUsers = GetFilteredUsers();
                }
                return _filteredUsers;
            }
        }

        #endregion

        #region Entry Points

        /// <summary>
        /// Standard ctor.
        /// </summary>
        public RoleUserHierarchy(CachingCrmService svcHandle)
        {
            _svcHandle = svcHandle;
        }

        /// <summary>
        /// Reset the class instance
        /// </summary>
        public void Clear()
        {
            this._allRoles = null;
            this._allUsers = null;
            this._filteredRoles = null;
            this._filteredUsers = null;
        }

        #endregion

        #region Local Methods

        /// <summary>
        /// Filter users by BU
        /// </summary>
        private Dictionary<Guid, SystemUser> GetFilteredUsers()
        {
            Dictionary<Guid, SystemUser> list = new Dictionary<Guid, SystemUser>();

            foreach (SystemUser su in this.AllUsers.Values)
            {
                if (this.BuFilter != null)
                {
                    if (this.BuFilter.ContainsKey(su.BusinessUnitId))
                    {
                        list.Add(su.Id, su);
                    }
                }
                else
                {
                    list.Add(su.Id, su);
                }
            }

            return list;
        }

        /// <summary>
        /// Filter roles by BU (only roots)
        /// </summary>
        private Dictionary<Guid, SystemRole> GetFilteredRoles()
        {
            Dictionary<Guid, SystemRole> list = new Dictionary<Guid, SystemRole>();

            if (this.BuFilter != null)
            {
                foreach (SystemRole sr in this.AllRoles.Values)
                {
                    if (this.BuFilter.ContainsKey(sr.BusinessUnitId))
                    {
                        // this role must be added, but we insert the root instead (as long as it's not there yet)
                        if (!list.ContainsKey(sr.RootRoleId))
                        {
                            list.Add(sr.RootRoleId, sr.RootRole);
                        }
                    }
                }
            }
            else
            {
                foreach (SystemRole sr in this.AllRoles.Values)
                {
                    if (sr.ParentRoleId == null)
                    {
                        list.Add(sr.Id, sr);
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// Set parents, roots etc. in the role list
        /// </summary>
        private void SetupRoleHierarchy()
        {
            // clear derived and setup them up to contain selves
            foreach (SystemRole sr in this.AllRoles.Values)
            {
                sr.DerivedRoles.Clear();
                sr.DerivedRoles.Add(sr.Id, sr);
            }

            // set parent roles
            foreach (SystemRole sr in this.AllRoles.Values)
            {
                if (sr.ParentRoleId != null)
                {
                    sr.ParentRole = this.AllRoles[(Guid)sr.ParentRoleId];
                    sr.RootRole = sr.ParentRole; // temporarily set the parent as the root
                }
            }

            // resolve the root nodes
            bool modified = true;
            while (modified)
            {
                modified = false;
                foreach (SystemRole sr in this.AllRoles.Values)
                {
                    if (sr.RootRole != null && sr.RootRole.ParentRole != null)
                    {
                        sr.RootRole = sr.RootRole.ParentRole;
                        modified = true;
                    }
                }
            }

            // assign root ids
            foreach (SystemRole sr in this.AllRoles.Values)
            {
                if (sr.RootRole != null)
                {
                    sr.RootRoleId = sr.RootRole.Id;
                }
                else
                {
                    sr.RootRole = sr;
                    sr.RootRoleId = sr.Id;
                }
            }

            // aggregate derived on the parent and set derived BU
            foreach (SystemRole sr in this.AllRoles.Values)
            {
                foreach (SystemRole derived in sr.GetDerivedRoles(this.AllRoles).Values)
                {
                    sr.DerivedRoles.Add(derived.Id, derived);
                }

                sr.DerivedBUs = new HashSet<Guid>();
                foreach (SystemRole derived in sr.DerivedRoles.Values)
                {
                    sr.DerivedBUs.Add(derived.BusinessUnitId);
                }
            }
        }

        #endregion

    }
}
